"""
数据管理器

统一管理数据获取、存储、缓存功能，支持多交易所和配置驱动
"""

import time
import os
from datetime import datetime, timedelta
from typing import List, Dict, Any, Optional
from pathlib import Path
import pandas as pd
from .data_storage import DataStorage
from .data_fetcher import DataFetcher
from .data_cache import DataCache
from ..core import unified_logger
from ..core.exceptions import DataMissingError, ValidationError, NetworkError, XCQuantError
from ..utils import TimeUtils


class DataManager:
    """增强版数据管理器"""
    
    def __init__(self, config: Dict[str, Any]):
        """
        初始化数据管理器
        
        Args:
            config: 数据管理配置
        """
        # 基础配置
        self.config = config
        storage_path = config.get('data_path', './data')
        cache_size = config.get('cache_size', 10000)
        
        # 初始化组件
        self.storage = DataStorage(storage_path)
        self.cache = DataCache(cache_size)
        self.logger = unified_logger.get_logger('data_manager')
        self._fetchers: Dict[str, DataFetcher] = {}
        
        # 数据配置
        self.auto_download = config.get('auto_download', True)
        self.data_sources = config.get('data_sources', ['okx', 'binance'])
        self.time_format = config.get('time_format', 'iso')
        
        # 存储结构配置
        self.storage_structure = config.get('storage_structure', {
            'by_exchange': True,
            'by_type': True,
            'by_symbol': True,
            'by_interval': True
        })
        
        # 创建数据目录结构
        self._create_data_directories()
    
    def _create_data_directories(self) -> None:
        """创建数据目录结构"""
        base_path = Path(self.storage.storage_path)
        base_path.mkdir(parents=True, exist_ok=True)
        
        # 按交易所创建目录
        for exchange in self.data_sources:
            exchange_path = base_path / exchange
            exchange_path.mkdir(exist_ok=True)
            
            # 按交易类型创建目录
            for trade_type in ['spot', 'futures']:
                type_path = exchange_path / trade_type
                type_path.mkdir(exist_ok=True)
        
        self.logger.info(f"数据目录结构已创建: {self.storage.storage_path}")
    
    def get_data_path(self, exchange: str, trade_type: str, 
                     symbol: str, interval: str) -> Path:
        """
        获取数据文件路径
        
        Args:
            exchange: 交易所名称
            trade_type: 交易类型 (spot/futures)
            symbol: 交易对
            interval: 时间间隔
            
        Returns:
            数据文件路径
        """
        base_path = Path(self.storage.storage_path)
        
        if self.storage_structure.get('by_exchange', True):
            base_path = base_path / exchange
        
        if self.storage_structure.get('by_type', True):
            base_path = base_path / trade_type
        
        if self.storage_structure.get('by_symbol', True):
            base_path = base_path / symbol.replace('-', '_').replace('/', '_')
        
        if self.storage_structure.get('by_interval', True):
            filename = f"{interval}.csv"
        else:
            filename = f"{symbol}_{interval}.csv"
        
        return base_path / filename
    
    def standardize_timestamp(self, timestamp: Any, unit: str = 'ms') -> datetime:
        """
        标准化时间戳
        
        Args:
            timestamp: 时间戳
            unit: 单位 ('ms', 's', 'ns')
            
        Returns:
            标准化的datetime对象
        """
        return TimeUtils.standardize_timestamp(timestamp, unit)
    
    def validate_data(self, data: pd.DataFrame, symbol: str, interval: str) -> bool:
        """验证数据完整性"""
        try:
            if data.empty:
                self.logger.warning(f"数据为空: {symbol} {interval}")
                return False
            
            # 检查必要列
            required_columns = ['datetime', 'timestamp', 'open', 'high', 'low', 'close', 'volume']
            missing_columns = [col for col in required_columns if col not in data.columns]
            if missing_columns:
                self.logger.error(f"缺少必要列: {missing_columns}")
                return False
            
            # 检查数据类型
            numeric_columns = ['open', 'high', 'low', 'close', 'volume']
            for col in numeric_columns:
                if not pd.api.types.is_numeric_dtype(data[col]):
                    self.logger.error(f"列 {col} 不是数值类型")
                    return False
            
            # 检查价格逻辑
            invalid_prices = (data['high'] < data['low']) | \
                           (data['open'] < 0) | (data['close'] < 0) | \
                           (data['high'] < 0) | (data['low'] < 0)
            
            if invalid_prices.any():
                self.logger.warning(f"发现无效价格数据: {invalid_prices.sum()} 条")
            
            # 检查时间连续性
            if len(data) > 1:
                time_diff = data['datetime'].diff().dropna()
                expected_interval = self._get_interval_timedelta(interval)
                
                # 允许一定的时间误差
                tolerance = expected_interval * 0.1
                irregular_intervals = abs(time_diff - expected_interval) > tolerance
                
                if irregular_intervals.any():
                    self.logger.warning(f"发现时间间隔不规律: {irregular_intervals.sum()} 处")
            
            self.logger.info(f"数据验证通过: {symbol} {interval}, {len(data)} 条记录")
            return True
            
        except NetworkError as e:
            self.logger.error(f"网络错误获取数据 {symbol}-{period}: {e}")
            # 尝试返回缓存的旧数据
            if use_cache:
                cached_data = self.cache.get(exchange, market_type, symbol, period)
                if cached_data is not None:
                    self.logger.warning(f"网络错误，返回缓存数据: {symbol}-{period}")
                    return cached_data
            raise DataMissingError(f"无法获取数据且无缓存可用: {symbol}-{period}")
        except ValidationError as e:
            self.logger.error(f"数据验证失败 {symbol}-{period}: {e}")
            return None
        except Exception as e:
            self.logger.error(f"数据验证失败: {e}")
            return False
    
    def _get_interval_timedelta(self, interval: str) -> timedelta:
        """
        获取时间间隔的timedelta对象
        
        Args:
            interval: 时间间隔字符串
            
        Returns:
            timedelta对象
        """
        return TimeUtils.get_interval_timedelta(interval)
    
    def fill_missing_data(self, data: pd.DataFrame, symbol: str, interval: str) -> pd.DataFrame:
        """填充缺失数据"""
        try:
            if data.empty:
                return data
            
            # 创建完整的时间序列
            start_time = data['datetime'].min()
            end_time = data['datetime'].max()
            interval_delta = self._get_interval_timedelta(interval)
            
            full_time_range = pd.date_range(
                start=start_time, 
                end=end_time, 
                freq=interval_delta
            )
            
            # 创建完整的DataFrame
            full_df = pd.DataFrame({'datetime': full_time_range})
            full_df['timestamp'] = full_df['datetime'].astype('int64') // 10**6
            
            # 合并数据
            merged_df = full_df.merge(data, on='datetime', how='left', suffixes=('', '_orig'))
            
            # 填充缺失值
            numeric_columns = ['open', 'high', 'low', 'close', 'volume']
            for col in numeric_columns:
                if col in merged_df.columns:
                    # 使用前向填充
                    merged_df[col] = merged_df[col].fillna(method='ffill')
                    # 如果还有缺失值，使用后向填充
                    merged_df[col] = merged_df[col].fillna(method='bfill')
            
            # 更新timestamp
            merged_df['timestamp'] = merged_df['timestamp'].fillna(
                merged_df['datetime'].astype('int64') // 10**6
            )
            
            filled_count = merged_df.isna().sum().sum() - data.isna().sum().sum()
            if filled_count > 0:
                self.logger.info(f"填充了 {filled_count} 个缺失数据点: {symbol} {interval}")
            
            return merged_df[['datetime', 'timestamp', 'open', 'high', 'low', 'close', 'volume']]
            
        except Exception as e:
            self.logger.error(f"填充缺失数据失败: {e}")
            return data
    
    def download_and_save_data(self, exchange: str, symbol: str, interval: str,
                              trade_type: str = 'spot', 
                              start_time: Optional[datetime] = None,
                              end_time: Optional[datetime] = None) -> bool:
        """
        下载并保存数据
        
        Args:
            exchange: 交易所名称
            symbol: 交易对
            interval: 时间间隔
            trade_type: 交易类型
            start_time: 开始时间
            end_time: 结束时间
            
        Returns:
            是否成功
        """
        try:
            if exchange not in self._fetchers:
                raise DataError(f"未注册的交易所: {exchange}")
            
            fetcher = self._fetchers[exchange]
            
            # 获取数据
            data = fetcher.fetch_kline_data(symbol, interval, start_time, end_time)
            
            if not data:
                self.logger.warning(f"未获取到数据: {exchange} {symbol} {interval}")
                return False
            
            # 标准化时间戳
            for item in data:
                if 'timestamp' in item:
                    item['timestamp'] = self.standardize_timestamp(item['timestamp'])
                    # 添加可读时间
                    if isinstance(item.get('timestamp'), str) and item['timestamp'].isdigit():
                        dt = datetime.fromtimestamp(int(item['timestamp']) / 1000)
                    else:
                        dt = datetime.fromisoformat(item['timestamp'].replace('Z', '+00:00'))
                    item['datetime'] = dt.strftime('%Y-%m-%d %H:%M:%S')
            
            # 保存到指定路径
            file_path = self.get_data_path(exchange, trade_type, symbol, interval)
            file_path.parent.mkdir(parents=True, exist_ok=True)
            
            df = pd.DataFrame(data)
            df.to_csv(file_path, index=False)
            
            self.logger.info(f"数据已保存: {file_path} ({len(data)} 条记录)")
            return True
            
        except Exception as e:
            self.logger.error(f"下载并保存数据失败: {e}")
            return False
    
    def register_exchange(self, exchange_name: str, exchange_adapter):
        """
        注册交易所适配器
        
        Args:
            exchange_name: 交易所名称
            exchange_adapter: 交易所适配器实例
        """
        self._fetchers[exchange_name] = DataFetcher(exchange_adapter)
        self.logger.info(f"注册交易所适配器: {exchange_name}")
    
    def get_kline_data(self, exchange: str, market_type: str, symbol: str, 
                      period: str, start_time: Optional[int] = None, 
                      end_time: Optional[int] = None, 
                      use_cache: bool = True) -> Optional[pd.DataFrame]:
        """
        获取K线数据
        
        Args:
            exchange: 交易所名称
            market_type: 市场类型 (spot/futures)
            symbol: 交易对
            period: 时间周期
            start_time: 开始时间戳
            end_time: 结束时间戳
            use_cache: 是否使用缓存
            
        Returns:
            K线数据DataFrame
        """
        try:
            # 首先尝试从缓存获取
            if use_cache:
                cached_data = self.cache.get(exchange, market_type, symbol, period)
                if cached_data is not None:
                    # 如果指定了时间范围，进行过滤
                    if start_time is not None or end_time is not None:
                        filtered_data = cached_data.copy()
                        if start_time is not None:
                            filtered_data = filtered_data[filtered_data['timestamp'] >= start_time]
                        if end_time is not None:
                            filtered_data = filtered_data[filtered_data['timestamp'] <= end_time]
                        return filtered_data
                    return cached_data
            
            # 从本地存储加载
            data = self.storage.load_kline_data(
                exchange, market_type, symbol, period, start_time, end_time
            )
            
            if data is not None and len(data) > 0:
                # 缓存数据
                if use_cache:
                    self.cache.put(exchange, market_type, symbol, period, data)
                return data
            
            # 如果本地没有数据，尝试从交易所获取
            if exchange in self._fetchers:
                self.logger.info(f"本地无数据，从交易所获取: {symbol}-{period}")
                return self._fetch_and_store_data(
                    exchange, market_type, symbol, period, start_time, end_time
                )
            
            self.logger.warning(f"未找到数据且无可用的数据获取器: {exchange}")
            return None
            
        except Exception as e:
            from ..core.error_handler import error_handler
            import asyncio
            
            # 使用新的错误处理机制
            try:
                loop = asyncio.get_event_loop()
                return loop.run_until_complete(
                    error_handler.handle_error(e, "DataManager", "get_kline_data", {
                        'exchange': exchange,
                        'market_type': market_type,
                        'symbol': symbol,
                        'period': period,
                        'start_time': start_time,
                        'end_time': end_time
                    })
                )
            except Exception:
                # 如果错误处理器也失败，回退到原始处理
                self.logger.error(f"获取K线数据失败: {e}")
                return None
    
    def _fetch_and_store_data(self, exchange: str, market_type: str, 
                             symbol: str, period: str,
                             start_time: Optional[int] = None, 
                             end_time: Optional[int] = None) -> Optional[pd.DataFrame]:
        """从交易所获取数据并存储"""
        try:
            fetcher = self._fetchers[exchange]
            
            # 如果没有指定时间范围，获取最近的数据
            if start_time is None or end_time is None:
                end_time = int(time.time())
                # 根据时间频度调整默认获取的数据量
                if period in ['1m', '3m', '5m']:
                    # 分钟级数据，获取7天
                    start_time = end_time - 86400 * 7
                elif period in ['15m', '30m']:
                    # 15分钟和30分钟数据，获取30天
                    start_time = end_time - 86400 * 30
                elif period in ['1h', '2h', '4h']:
                    # 小时级数据，获取90天
                    start_time = end_time - 86400 * 90
                elif period in ['6h', '8h', '12h']:
                    # 更长时间间隔，获取180天
                    start_time = end_time - 86400 * 180
                elif period in ['1d', '3d']:
                    # 日级数据，获取一年
                    start_time = end_time - 86400 * 365
                elif period in ['1w']:
                    # 周级数据，获取两年
                    start_time = end_time - 86400 * 365 * 2
                else:
                    # 默认获取一年数据
                    start_time = end_time - 86400 * 365
            
            # 获取数据
            raw_data = fetcher.fetch_kline_data(symbol, period, start_time, end_time)
            
            if raw_data:
                # 存储数据
                success = self.storage.save_kline_data(
                    exchange, market_type, symbol, period, raw_data
                )
                
                if success:
                    # 重新加载并缓存
                    data = self.storage.load_kline_data(
                        exchange, market_type, symbol, period, start_time, end_time
                    )
                    if data is not None:
                        self.cache.put(exchange, market_type, symbol, period, data)
                        return data
            
            return None
            
        except Exception as e:
            self.logger.error(f"获取并存储数据失败: {e}")
            return None
    
    def update_data(self, exchange: str, market_type: str, symbol: str, 
                   period: str, force_update: bool = False) -> bool:
        """
        更新数据
        
        Args:
            exchange: 交易所名称
            market_type: 市场类型
            symbol: 交易对
            period: 时间周期
            force_update: 是否强制更新
            
        Returns:
            是否更新成功
        """
        try:
            if exchange not in self._fetchers:
                self.logger.error(f"未注册的交易所: {exchange}")
                return False
            
            # 获取数据信息
            data_info = self.storage.get_data_info(exchange, market_type, symbol, period)
            
            # 确定更新时间范围
            if data_info and not force_update:
                # 增量更新：从最后时间戳开始
                start_time = data_info['last_timestamp']
                end_time = int(time.time())
            else:
                # 全量更新：获取最近30天数据
                end_time = int(time.time())
                start_time = end_time - 86400 * 30
            
            # 获取新数据
            fetcher = self._fetchers[exchange]
            raw_data = fetcher.fetch_kline_data(symbol, period, start_time, end_time)
            
            if raw_data:
                # 存储数据
                success = self.storage.save_kline_data(
                    exchange, market_type, symbol, period, raw_data
                )
                
                if success:
                    # 使缓存失效
                    self.cache.invalidate(exchange, market_type, symbol, period)
                    self.logger.info(f"数据更新成功: {symbol}-{period}")
                    return True
            
            return False
            
        except Exception as e:
            self.logger.error(f"更新数据失败: {e}")
            return False
    
    def fill_missing_data(self, exchange: str, market_type: str, symbol: str, 
                         period: str, start_date: str, end_date: str) -> bool:
        """
        补全缺失数据
        
        Args:
            exchange: 交易所名称
            market_type: 市场类型
            symbol: 交易对
            period: 时间周期
            start_date: 开始日期 (YYYYMMDD)
            end_date: 结束日期 (YYYYMMDD)
            
        Returns:
            是否补全成功
        """
        try:
            if exchange not in self._fetchers:
                self.logger.error(f"未注册的交易所: {exchange}")
                return False
            
            # 获取缺失日期
            missing_dates = self.storage.get_missing_dates(
                exchange, market_type, symbol, period, start_date, end_date
            )
            
            if not missing_dates:
                self.logger.info(f"无缺失数据: {symbol}-{period}")
                return True
            
            self.logger.info(f"发现 {len(missing_dates)} 个缺失日期，开始补全")
            
            fetcher = self._fetchers[exchange]
            success_count = 0
            
            for date_str in missing_dates:
                try:
                    # 转换日期为时间戳
                    date_obj = datetime.strptime(date_str, '%Y%m%d')
                    start_time = int(date_obj.timestamp())
                    end_time = int((date_obj + timedelta(days=1)).timestamp())
                    
                    # 获取数据
                    raw_data = fetcher.fetch_kline_data(
                        symbol, period, start_time, end_time
                    )
                    
                    if raw_data:
                        # 存储数据
                        success = self.storage.save_kline_data(
                            exchange, market_type, symbol, period, raw_data
                        )
                        if success:
                            success_count += 1
                    
                    # 避免API限制
                    time.sleep(0.1)
                    
                except Exception as e:
                    self.logger.warning(f"补全日期 {date_str} 失败: {e}")
                    continue
            
            # 使缓存失效
            self.cache.invalidate(exchange, market_type, symbol, period)
            
            self.logger.info(f"数据补全完成: {success_count}/{len(missing_dates)}")
            return success_count > 0
            
        except Exception as e:
            self.logger.error(f"补全数据失败: {e}")
            return False
    
    def get_latest_price(self, exchange: str, symbol: str) -> Optional[float]:
        """
        获取最新价格
        
        Args:
            exchange: 交易所名称
            symbol: 交易对
            
        Returns:
            最新价格
        """
        try:
            if exchange not in self._fetchers:
                return None
            
            fetcher = self._fetchers[exchange]
            return fetcher.exchange.get_latest_price(symbol)
            
        except Exception as e:
            self.logger.error(f"获取最新价格失败: {e}")
            return None
    
    def get_data_stats(self) -> Dict[str, Any]:
        """
        获取数据统计信息
        
        Returns:
            数据统计信息
        """
        try:
            storage_stats = self.storage.get_storage_stats()
            cache_stats = self.cache.get_cache_stats()
            
            return {
                'storage': storage_stats,
                'cache': cache_stats,
                'registered_exchanges': list(self._fetchers.keys())
            }
            
        except Exception as e:
            self.logger.error(f"获取数据统计失败: {e}")
            return {}
    
    def cleanup_cache(self) -> None:
        """清理缓存"""
        self.cache.clear()
        self.logger.info("缓存已清理")